package com.glodon.paas.account.web.controller;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.glodon.paas.account.api.bean.User;
import com.glodon.paas.account.api.bean.AccountConstant;
import com.glodon.paas.account.security.spring.LoginUser;
import com.glodon.paas.account.security.spring.login.AuthenticationSuccessHandler;
import com.glodon.paas.account.service.UserService;
import com.glodon.paas.account.util.captcha1.CaptchaService;
import com.glodon.paas.account.web.util.ImageUtil;
import com.glodon.paas.account.web.util.SecurityUtils;
import com.glodon.paas.consts.StringConst;
import com.glodon.paas.exception.PaasServerExceptionFactory;
import com.glodon.paas.exception.i18n.ErrorCode;


@Controller
@RequestMapping("/")
public class UserController{ 
	Map<Integer, byte[]> defaultAvatarMap = new HashMap<Integer,byte[]>();
	
	@Qualifier("userAuthenticationManager")
	@Autowired
	private AuthenticationManager userAuthenticationManager;
	
	public static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);

	@Autowired
	private UserService userService; 
	@Value("${account.avatar.sizes}")
	String defaultAvatarSizeStr;
	
	Set<Integer> defaultAvatarSizeSet;
	
	@Autowired
	CaptchaService captchaService;

	@Autowired
	private AuthenticationSuccessHandler handler;

	private Set<Integer> getDefaultAvatarSize(){
		if(defaultAvatarSizeSet!=null){
			return defaultAvatarSizeSet;
		}
		defaultAvatarSizeSet = new HashSet<Integer>();
		
		if(StringUtils.isBlank(defaultAvatarSizeStr)){
			defaultAvatarSizeStr = "80";
		}
		
		String[] sizes = defaultAvatarSizeStr.split(",");
		for(String size : sizes){			
			try {
				size = size.trim();
				defaultAvatarSizeSet.add(Integer.parseInt(size));
			} catch (Exception e) {
				LOGGER.error("setDefaultAvatarSize error, ["+size+"]can not parse to int"  ,e);
			}
			
		}
		return defaultAvatarSizeSet;
	}
	
	
	@RequestMapping(value = "/info", method = RequestMethod.GET)
	public ModelAndView viewUser(HttpServletRequest request,
			HttpServletResponse response, ModelMap model)
			{
		User dbUser = userService.getUserInfoById(SecurityUtils.getCurrentUser().getId());
		model.addAttribute("user", dbUser);
		request.setAttribute("glodonId", dbUser.getEmail());
		request.setAttribute("year", dbUser.year());
		request.setAttribute("month", dbUser.month());
		request.setAttribute("day", dbUser.day());
		getAccessTokens(model);
		getOAuth2Tokens(model);
		return new ModelAndView("userinfo", model);
	}

	@RequestMapping(value = "/addUser", method = RequestMethod.POST)
	public void addUser(
			@RequestParam(value="accountName", required=false) String accountName,
			//@RequestParam(value="accountType", required=false) Integer accountType, 
			@RequestParam(value="password", required=true) String password,
			@RequestParam(value="passwordConfirm", required=true) String passwordConfirm,
			@RequestParam(value="captchaValue", required=true) String captchaValue, 
			@RequestParam(value="userId", required=false) String userId, 
			HttpServletRequest request, 
			HttpServletResponse response) {
		Integer accountType = null;
		try{
			if(StringUtils.isBlank(password) || !password.equals(passwordConfirm)){
				throw PaasServerExceptionFactory.validateException("passwordNotMatch", null).setErrorCode(ErrorCode.PASSWORD_CONFIRM_ERROR);
			}
			Boolean rt = captchaService.verifyCaptcha(request.getSession().getId(), captchaValue, true);
			if(rt == null || !rt.booleanValue()){
				throw PaasServerExceptionFactory.validateException("CaptchaError", null).setErrorCode(ErrorCode.CAPTCHA_CODE_ERR);
			}
			
			if(StringUtils.isEmpty(userId)){
				userId = userService.registerAccount(accountName, accountType, password, AccountConstant.USER_STATUS_ACTIVE).getId();
			}else{
				userService.activeUser(userId, accountName, accountType, password);			
			} 
			
			try {
				Authentication authentication = authenticateUserAndSetSession(accountName, password, request);
				handler.onAuthenticationSuccess(request, response, authentication);
			} catch (Exception e) {
				LOGGER.debug(e.getMessage(), e);
				String url = request.getRequestURL().toString().replace("/addUser", "/login");
				String loginUrlQueryString = (String)request.getSession().getAttribute("loginUrlQueryString");
				if(StringUtils.isNotEmpty(loginUrlQueryString)){
					url+="?"+loginUrlQueryString;
				}
				try {
					response.sendRedirect(url);
				} catch (IOException e1) {
					LOGGER.error(e1.getMessage(),e1);
				}
			}
		}catch (Exception e) {
			String url = request.getRequestURL().toString().replace("/addUser", "/register?error=true");
			String loginUrlQueryString = (String)request.getSession().getAttribute("loginUrlQueryString");
			if(StringUtils.isNotEmpty(loginUrlQueryString)){
				url+="&"+loginUrlQueryString;
			}
			try {
				response.sendRedirect(url);
			} catch (IOException e1) {
				LOGGER.error(e1.getMessage(),e1);
			}
		}
	}

	@RequestMapping(value = "/getUser", method = RequestMethod.GET)
	public void getUser(
			@RequestParam(value="accountName", required=false) String accountName, 
			@RequestParam(value="userId", required=false) String userId,
			ModelMap model) {
		
		if(StringUtils.isNotBlank(userId)){
			 User user = userService.getUserById(userId);
			 model.addAttribute(StringConst.RESULT, user);
			 return;
		 }
		
		 if(StringUtils.isNotBlank(accountName)){
			 User user = userService.getUserByAccountName(accountName, null, null);
			 model.addAttribute(StringConst.RESULT, user);
			 return;
		 }		 
	}
	
	private Authentication authenticateUserAndSetSession(String accountName, String password, HttpServletRequest request) {
		
		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(accountName, password);
		// generate session if one doesn't exist
		request.getSession();
		token.setDetails(new WebAuthenticationDetails(request));

		//Authentication authenticatedUser = ((ProviderManager) BeanFactoryUtil.getBean("userAuthenticationManager")).authenticate(token);
		Authentication authenticatedUser = userAuthenticationManager.authenticate(token);

		SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
		return authenticatedUser;
	}

	@RequestMapping(value = "/updateUser", method = RequestMethod.POST)
	public void updateUser(User u, ModelMap model) {
		u.setId(SecurityUtils.getCurrentUser().getId());
		userService.updateUser(u);
		//TODO 修改缓存变量中的变量，当前不考虑分布式问题,主要是前端显示名问题
		updateCachedCurrentUser(u);
		model.addAttribute(StringConst.RESULT, StringConst.SUCCESS);
	}
	
	private void updateCachedCurrentUser(User u){
		LoginUser cacheUser = SecurityUtils.getCurrentUser();
		cacheUser.setNickname(u.getNickname());
		if(u.getNickname() != null){
			cacheUser.setNickname(u.getNickname());
		}
		if(u.getEmail() != null){
			cacheUser.setEmail(u.getEmail());
		}
		if(u.getMobile() != null){
			cacheUser.setMobile(u.getMobile());
		}
	}

	@RequestMapping(value = "/avatarUpload", method = RequestMethod.POST)
	public void avatarUpload(
			@RequestParam(value="x",required=false) String fx,
			@RequestParam(value="y",required=false) String fy,
			@RequestParam(value="w",required=false) String fw,
			@RequestParam(value="h",required=false) String fh,
			@RequestParam(value="notcrop",required=false) String notCrop,
			HttpServletRequest request, 
			ModelMap model) {
		String userId = SecurityUtils.getCurrentUser().getId();
		
		// 文件上传
		byte[] upImage = null;
		if (ServletFileUpload.isMultipartContent(request)){
			try{
				DiskFileItemFactory df = new DiskFileItemFactory();
				ServletFileUpload upload = new ServletFileUpload(df);
				FileItemIterator iter = upload.getItemIterator(request);
				while (iter.hasNext()) {
					FileItemStream item = iter.next();
					//String name = item.getFieldName();
					InputStream instream = item.openStream();
					if (!item.isFormField()) {
						BufferedImage original = ImageIO.read(instream);
						ByteArrayOutputStream out = new ByteArrayOutputStream();
					    ImageIO.write(original, "png", out); 
					    upImage = out.toByteArray();
					    // 原始图片存为临时文件
					    userService.saveAvatar(userId+"_1", null,null, upImage);
					} 
					instream.close();
				}
				model.addAttribute(StringConst.RESULT, StringConst.SUCCESS);
				return;
			}catch (Exception e) {
				 throw PaasServerExceptionFactory.systemException(e.getMessage(), e);
			}
		}else{
			//LOGGER.error("upload file is not multipartContent .");
		}
		
		// 文件裁剪
		if(fx != null){
			float floatx = Float.valueOf(fx);
			float floaty = Float.valueOf(fy);
			float floatw = Float.valueOf(fw);
			float floath = Float.valueOf(fh);
			
			// 获取临时文件
			if(upImage == null){
				upImage = userService.getAvatar(userId+"_1",userId, null);
			}
			// 原始图片存为正式文件
			userService.saveAvatar(userId+"_0", userId, 0, upImage);
			// 裁剪
			upImage = ImageUtil.cutting(upImage, (int)floatx, (int)floaty, (int)floatw, (int)floath);
			// 保存裁剪图片
			userService.saveAvatar(userId+"_2", userId, 2, upImage);

			// 删除旧缩略图
			Set<Integer> map = getDefaultAvatarSize();
			for(Integer size : map){
				if(size > 5){
					userService.deleteAvatar(userId+"_"+size, null, null);
				}				
			}
		}else if("true".equalsIgnoreCase(notCrop)){
			// 上传的头像比较小，不需要裁剪，前台不提交x,y,w,h提交notCrop=true
			if(upImage == null){
				upImage = userService.getAvatar(userId+"_1",null, null);
			}
			
			if(upImage != null){
				userService.saveAvatar(userId+"_2", null, null,upImage);
			}
			
			// 删除旧缩略图
			Set<Integer> map = getDefaultAvatarSize();
			for(Integer size : map){
				if(size > 5){
					userService.deleteAvatar(userId+"_"+size, null, null);
				}				
			}
		}
		
		
		model.addAttribute(StringConst.RESULT, StringConst.SUCCESS);
	}
	
	@SuppressWarnings("deprecation")
	@RequestMapping(value = "/avatar", method = RequestMethod.GET)
	public void getAvatar(
		  @RequestParam(value = "id", required = false)  String id,
		  HttpServletRequest request, 
  		  HttpServletResponse response,
  		  ModelMap model ) {
		String userId = SecurityUtils.getCurrentUser().getId();	
		
		int size = 0;
		if(id != null){
			try {
				String strSize = id.substring(id.lastIndexOf("_")+1);
				size = Integer.parseInt(strSize);
			} catch (Exception e) { }
		}else{
			id = userId + "_" + size;
		}
		
		// 大小
		Set<Integer> map = getDefaultAvatarSize();
		byte[] image = null;
		if(map.contains(size) || size == 0 || size == 1 ){
			//允许的size,数据库获取图片
			image = userService.getAvatar(id , null, null);
		}else{
			throw PaasServerExceptionFactory.validateException("invalided size:" + size, null);
		}
		
		if(image == null){
			//获取裁剪图片
			image = userService.getAvatar(userId+"_2" , null, null);
			//转成小图片
			try {
				image = ImageUtil.makeThumbnailByteArray(image, size, size,false);
			} catch (Exception e) {
				LOGGER.error("makeThumbnailByteArray error:"+e.getMessage(),e);
			}
			
			if(image != null){
				userService.saveAvatar(null, userId, size, image);
			}
		} 
		
		//加载默认图片
		if(image == null){
			image = loadDefaultAvatar(request.getRealPath("/"), size);
		}
		
		if(image == null){
			return;
		}

	  	//设置响应的类型格式为图片格式
		response.setContentType("image/jpeg");
		//禁止图像缓存。
		response.setHeader("Pragma", "no-cache");
		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);
		ServletOutputStream out = null;
		try {
			out = response.getOutputStream();
			out.write(image);
			out.flush();
			out.close();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
        return;
	}
	
	private byte[] loadDefaultAvatar(String realPath, int size){
		int maxSize = 200;
		byte[] temp = defaultAvatarMap.get(size);
		
		if(temp != null){
			return temp;
		}

		//原始默认图片
		temp = defaultAvatarMap.get(maxSize); 
		if(temp == null){ 	
			try {
				InputStream in = new FileInputStream(realPath + "/images/user_X176.png");
				temp = ImageUtil.makeThumbnailByteArray(in,maxSize,maxSize,false);
				defaultAvatarMap.put(maxSize, temp);
				in.close();
			} catch (Exception e) {
				LOGGER.error(e.getMessage(),e);
			}
		}
		
		if(!getDefaultAvatarSize().contains(size)){
			return null;
		}
		
		//转换成指定大小图片
		try {
			ByteArrayInputStream in = new ByteArrayInputStream(temp);
			temp = ImageUtil.makeThumbnailByteArray(in,maxSize,maxSize,false);
			defaultAvatarMap.put(size, temp);
			in.close();
		} catch (Exception e) {
			LOGGER.error(e.getMessage(),e);
		}
		return temp;
	}
	
	
	@RequestMapping(value = "/check", method = RequestMethod.POST)
	public void checkLoginName(
			@RequestParam("loginName") String accountName, 
			ModelMap model
			) {
		User user = userService.getUserByAccountName(accountName, null, AccountConstant.USER_STATUS_ACTIVE);
		 
		model.addAttribute(StringConst.RESULT, user == null);
		
		if( user != null )
		{
			model.addAttribute("id",user.getId());
		}
	}

	@RequestMapping(value = "/getAccessTokens", method = RequestMethod.GET)
	public void getAccessTokens(ModelMap model) {
		model.addAttribute("datas", userService.getAuthorizatedApps(SecurityUtils.getCurrentUser().getId()));
	}

	@RequestMapping(value = "/oauth2/getAccessTokens", method = RequestMethod.GET)
	public void getOAuth2Tokens(ModelMap model) {
		model.addAttribute("oauth2Info", userService.getAuthInfo(SecurityUtils.getCurrentUser().getId()));
	}
	
	@RequestMapping(value = "/cancelAuthorization", method = RequestMethod.POST)
	public void cancelAuthorization(@RequestParam("id") String tokenId, ModelMap model) {
		userService.cancelAuthorizationApp(tokenId);
		model.addAttribute(StringConst.RESULT, StringConst.SUCCESS);
	}
	
	public void close(Closeable obj){
		if(obj!=null){
			try {
				obj.close();
			} catch (Exception e) {
			}
		}
	}
}
